package com.wj.filter;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.wj.constant.CommonConstant;
import com.wj.constant.GatewayConstant;
import com.wj.util.TokenParseUtil;
import com.wj.vo.JwtToken;
import com.wj.vo.LoginUserInfo;
import com.wj.vo.UsernameAndPassword;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;

@Slf4j
@Component
public class GlobalLoginOrRegisterFilter implements GlobalFilter, Ordered {


    // 注册中心客户端，可以从注册中心中获取服务实例信息
    private final LoadBalancerClient loadBalancerClient;
    private final RestTemplate restTemplate;

    public GlobalLoginOrRegisterFilter(LoadBalancerClient loadBalancerClient, RestTemplate restTemplate) {
        this.loadBalancerClient = loadBalancerClient;
        this.restTemplate = restTemplate;
    }


    /**
     * 登录、注册、鉴权
     *  1. 如果是登录或注册、则去授权中心拿到 Token 并返回给客户端
     *  2. 如果是访问其他服务、则进行鉴权，没有权限则返回 401
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        // 1：如果是登录
        if (request.getURI().getPath().contains(GatewayConstant.LOGIN_URI)){
            // 1.1：去授权中心去拿 Token
            String token = getTokenFromAuthorityCenter(request,GatewayConstant.AUTHORITY_CENTER_TOKEN_URI_FORMAT);

            //1.2 响应中添加 token
            response.getHeaders().add(CommonConstant.JWT_USER_INFO_KEY, StrUtil.isBlank(token)? "null" : token);
            response.setStatusCode(HttpStatus.OK);
            return response.setComplete();
        }

        // 2：如果是注册
        if (request.getURI().getPath().contains(GatewayConstant.REGISTRY_URI)){
            // 2.1：去授权中心去拿 Token：先创建用户，在返回 Token
            String token = getTokenFromAuthorityCenter(request,GatewayConstant.AUTHORITY_CENTER_REGISTRY_URI_FORMAT);

            //2.2 响应中添加 token
            response.getHeaders().add(CommonConstant.JWT_USER_INFO_KEY, StrUtil.isBlank(token)? "null" : token);
            response.setStatusCode(HttpStatus.OK);
            return response.setComplete();
        }

        // 3：带着 Jwt Token 去访问其他服务，则进行鉴权，校验是否能从 Token 中解析出用户信息
        HttpHeaders headers = request.getHeaders();
        String token = headers.getFirst(CommonConstant.JWT_USER_INFO_KEY);
        LoginUserInfo loginUserInfo = null;
        try {
            loginUserInfo = TokenParseUtil.parseUserInfoFromToken(token);
        } catch (Exception e) {
            log.error("parse user info from token error: [{}]",e.getMessage(),e);
        }

        // 获取不到登录用户信息，返回 401
        if (Objects.isNull(loginUserInfo)){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        // 解析通过，则放行
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE + 2;
    }

    private String getTokenFromAuthorityCenter(ServerHttpRequest request,String uriFormat){
        // service id 就是服务名字, 负载均衡
        ServiceInstance serviceInstance = loadBalancerClient.choose(
                CommonConstant.AUTHORITY_CENTER_SERVICE_ID
        );
        log.info("Nacos Client Info: [{}, [{}], [{}]",
                serviceInstance.getServiceId(),
                serviceInstance.getInstanceId(),
                serviceInstance.getMetadata());

        String requestUrl = String.format(uriFormat,serviceInstance.getHost(),serviceInstance.getPort());
        UsernameAndPassword requestBody = JSON.parseObject(parseBodyFromRequest(request), UsernameAndPassword.class);
        log.info("login request url and body: [{}], [{}]",requestBody,JSON.toJSONString(requestBody));

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        JwtToken token = restTemplate.postForObject(requestUrl,new HttpEntity<>(JSON.toJSONString(requestBody),headers),
                JwtToken.class);

        return Objects.isNull(token)? null : token.getToken();
    }

    /**
     * 从 post 请求中获取到请求数据
     *
     * @param request
     * @return
     */
    private String parseBodyFromRequest(ServerHttpRequest request){
        //获取请求体
        Flux<DataBuffer> body = request.getBody();
        AtomicReference<String> bodyRef = new AtomicReference<>();

        // 订阅缓冲区去消费请求体中的数据
        body.subscribe(buffer -> {
            CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
            //释放掉缓冲区的数据，防止内存泄漏
            DataBufferUtils.release(buffer);
            bodyRef.set(charBuffer.toString());
        });

        // 获取 request body
        return bodyRef.get();
    }
}
